package com.example.hotel.blImpl.security;

import com.example.hotel.bl.security.TokenService;
import com.example.hotel.vo.LoginUser;
import com.example.hotel.vo.Token;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.thymeleaf.util.MapUtils;

import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@Service
public class TokenServiceImpl implements TokenService {

    @Value("${token.expire.seconds}")
    private Integer expireSeconds;

    @Value("${token.jwtSecret}")
    private String jwtSecret;

    private Map<String, LoginUser> loginUserMap;

    private static final String LOGIN_USER_KEY = "LOGIN_USER_KEY";
    private static Key KEY = null;

    public TokenServiceImpl() {
        loginUserMap = new HashMap<>();
    }

    @Override
    public Token saveToken(LoginUser loginUser) {
        String token = UUID.randomUUID().toString();
        loginUser.setToken(token);
        cacheLoginUser(loginUser);

        String jwtToken = getJwtToken(loginUser);
        return new Token(jwtToken, loginUser.getLoginTime());
    }

    public void cacheLoginUser(LoginUser loginUser) {
        loginUser.setLoginTime(System.currentTimeMillis());
        loginUser.setExpireTime(loginUser.getLoginTime() + expireSeconds * 1000);
        // 缓存
        // 有效期功能使用模拟...
        // 由于未使用带过期功能的数据库(Redis等) 只好在此处进行过期的模拟
        loginUserMap.put(getTokenKey(loginUser.getToken()), loginUser);

    }

    @Override
    public void refresh(LoginUser loginUser) {
        cacheLoginUser(loginUser);
    }

    @Override
    public LoginUser getLoginUser(String jwtToken) {
        // 模拟过期
        String uuidFromJWT = getUUIDFromJWT(jwtToken);
        LoginUser loginUser = loginUserMap.get(getTokenKey(uuidFromJWT));
        if (loginUser == null) return null;

        // 过期在此处处理
        if (loginUser.getExpireTime() < System.currentTimeMillis()) {
            deleteToken(jwtToken);
            return null;
        }

        return loginUser;
    }

    @Override
    public boolean deleteToken(String jwtToken) {
        String uuid = getUUIDFromJWT(jwtToken);
        if (uuid != null) {
            String key = getTokenKey(uuid);
            LoginUser loginUser = loginUserMap.get(key);
            if (loginUser != null) {
                loginUserMap.remove(key);
                return true;
            }
        }
        return false;
    }

    public String getTokenKey(String token) {
        return "tokens:" + token;
    }

    private String getJwtToken(LoginUser loginUser) {
        Map<String, Object> claims = new HashMap<>();
        claims.put(LOGIN_USER_KEY, loginUser.getToken());
        // 生成JWT
        return Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS256, getKeyInstance()).compact();
    }

    private Key getKeyInstance() {
        // 单例
        if (KEY == null) {
            synchronized (TokenServiceImpl.class) {
                if (KEY == null) {
                    byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(jwtSecret);
                    KEY = new SecretKeySpec(apiKeySecretBytes, SignatureAlgorithm.HS256.getJcaName());
                }
            }
        }
        return KEY;
    }

    private String getUUIDFromJWT(String jwtToken) {
        if ("null".equals(jwtToken) || StringUtils.isEmpty(jwtToken))
            return null;

        try {
            // parser: 生成JWTClaims
            Map<String, Object> jwtClaims = Jwts.parser()
                    .setSigningKey(getKeyInstance())
                    .parseClaimsJws(jwtToken).getBody();
            return (String) jwtClaims.get(LOGIN_USER_KEY);
        } catch (Exception e) {
            System.out.println(e);
        }

        return null;
    }
}
